'''
Created on Mar 6, 2013

@author: larsonmp
'''

_HUFINAL_DICT = {
      1: 'FULLY COMPLETE CATI INTERVIEW',
      2: 'PARTIALLY COMPLETED CATI INTERVIEW',
      3: 'COMPLETE BUT PERSONAL VISIT REQUESTED NEXT MONTH',
      4: 'PARTIAL, NOT COMPLETE AT CLOSEOUT',
      5: 'LABOR FORCE COMPLETE, SUPPLEMENT INCOMPLETE - CATI',
      6: 'LF COMPLETE, SUPPLEMENT DK ITEMS INCOMPLETE AT CLOSEOUT - ASEC ONLY',
     20: 'HH OCCUPIED ENTIRELY BY ARMED FORCES MEMBERS OR ALL UNDER 15 YEARS OF AGE',
    201: 'CAPI COMPLETE',
    202: 'CALLBACK NEEDED',
    203: 'SUFFICIENT PARTIAL - PRECLOSEOUT',
    204: 'SUFFICIENT PARTIAL - AT CLOSEOUT',
    205: 'LABOR FORCE COMPLETE, - SUPPL. INCOMPLETE - CAPI',
    213: 'LANGUAGE BARRIER',
    214: 'UNABLE TO LOCATE',
    216: 'NO ONE HOME',
    217: 'TEMPORARILY ABSENT',
    218: 'REFUSED',
    219: 'OTHER OCCUPIED - SPECIFY',
    223: 'ENTIRE HOUSEHOLD ARMED FORCES',
    224: 'ENTIRE HOUSEHOLD UNDER 15',
    225: 'TEMP. OCCUPIED W/PERSONS WITH URE',
    226: 'VACANT REGULAR',
    227: 'VACANT - STORAGE OF HHLD FURNITURE',
    228: 'UNFIT, TO BE DEMOLISHED',
    229: 'UNDER CONSTRUCTION, NOT READY',
    230: 'CONVERTED TO TEMP BUSINESS OR STORAGE',
    231: 'UNOCCUPIED TENT OR TRAILER SITE',
    232: 'PERMIT GRANTED - CONSTRUCTION NOT STARTED',
    233: 'OTHER - SPECIFY',
    240: 'DEMOLISHED',
    241: 'HOUSE OR TRAILER MOVED',
    242: 'OUTSIDE SEGMENT',
    243: 'CONVERTED TO PERM. BUSINESS OR STORAGE',
    244: 'MERGED',
    245: 'CONDEMNED',
    246: 'BUILT AFTER APRIL 1, 2000',
    247: 'UNUSED SERIAL NO./LISTING SHEET LINE',
    248: 'OTHER - SPECIFY',
    256: 'REMOVED DURING SUB-SAMPLING',
    257: 'UNIT ALREADY HAD A CHANCE OF SELECTION'
}
_HEFAMINC_DICT = {
    -3: 'REFUSED',
    -2: "DON'T KNOW",
    -1: 'BLANK',
     1: 'LESS THAN $5,000',
     2: '$5,000 TO $7,499',
     3: '$7,500 TO $9,999',
     4: '$10,000 TO $12,499',
     5: '$12,500 TO $14,999',
     6: '$15,000 TO $19,999',
     7: '$20,000 TO $24,999',
     8: '$25,000 TO $29,999',
     9: '$30,000 TO $34,999',
    10: '$35,000 TO $39,999',
    11: '$40,000 TO $49,999',
    12: '$50,000 TO $59,999',
    13: '$60,000 TO $74,999',
    14: '$75,000 TO $99,999',
    15: '$100,000 TO $149,999',
    16: '$150,000 OR MORE'
}

income_levels = {
    -3: 'REFUSED',
    -2: "DON'T KNOW",
    -1: 'BLANK',
     1: 'LESS THAN \$5,000',
     2: '\$5,000 TO \$7,499',
     3: '\$7,500 TO \$9,999',
     4: '\$10,000 TO \$12,499',
     5: '\$12,500 TO \$14,999',
     6: '\$15,000 TO \$19,999',
     7: '\$20,000 TO \$24,999',
     8: '\$25,000 TO \$29,999',
     9: '\$30,000 TO \$34,999',
    10: '\$35,000 TO \$39,999',
    11: '\$40,000 TO \$49,999',
    12: '\$50,000 TO \$59,999',
    13: '\$60,000 TO \$74,999',
    14: '\$75,000 TO \$99,999',
    15: '\$100,000 TO \$149,999',
    16: '\$150,000 OR MORE'
}

_HRINTSTA_DICT = {
    1: 'INTERVIEW',
    2: 'TYPE A NON-INTERVIEW',
    3: 'TYPE B NON-INTERVIEW',
    4: 'TYPE C NON-INTERVIEW'
}

_GESTCEN_DICT = {
    11: 'ME', 44: 'ND', 71: 'AR',
    12: 'NH', 45: 'SD', 72: 'LA',
    13: 'VT', 46: 'NE', 73: 'OK',
    14: 'MA', 47: 'KS', 74: 'TX',
    15: 'RI', 51: 'DE', 81: 'MT',
    16: 'CT', 52: 'MD', 82: 'ID',
    21: 'NY', 53: 'DC', 83: 'WY',
    22: 'NJ', 54: 'VA', 84: 'CO',
    23: 'PA', 55: 'WV', 85: 'NM',
    31: 'OH', 56: 'NC', 86: 'AZ',
    32: 'IN', 57: 'SC', 87: 'UT',
    33: 'IN', 58: 'GA', 88: 'NV',
    34: 'MI', 59: 'FL', 91: 'WA',
    35: 'WI', 61: 'KY', 92: 'OR',
    41: 'MN', 62: 'TN', 93: 'CA',
    42: 'IA', 63: 'AL', 94: 'AK',
    43: 'MO', 64: 'MA', 95: 'HI'
}

_PEIOXICD_DICT = {
      -1: 'BLANK',
     170: 'Crop production',
     180: 'Animal production',
     190: 'Forestry except logging',
     270: 'Logging',
     280: 'Fishing, hunting, and trapping',
     290: 'Support activities for agriculture and forestry',
     370: 'Oil and gas extraction',
     380: 'Coal mining',
     390: 'Metal ore mining',
     470: 'Nonmetallic mineral mining and quarrying',
     480: 'Not specified type of mining Part of',
     490: 'Support activities for mining',
     570: 'Electric power generation, transmission and distribution Pt.',
     580: 'Natural gas distribution Pt.',
     590: 'Electric and gas, and other combinations Pts.',
     670: 'Water, steam, air-conditioning, and irrigation systems',
     680: 'Sewage treatment facilities',
     690: 'Not specified utilities Part of',
     770: 'Construction',
    1070: 'Animal food, grain and oilseed milling',
    1080: 'Sugar and confectionery products',
    1090: 'Fruit and vegetable preserving and specialty food manufacturing',
    1170: 'Dairy product manufacturing',
    1180: 'Animal slaughtering and processing',
    1190: 'Retail bakeries',
    1270: 'Bakeries, except retail',
    1280: 'Seafood and other miscellaneous foods, n.e.c.',
    1290: 'Not specified food industries Part of',
    1370: 'Beverage manufacturing',
    1390: 'Tobacco manufacturing',
    1470: 'Fiber, yarn, and thread mills',
    1480: 'Fabric mills, except knitting',
    1490: 'Textile and fabric finishing and coating mills',
    1570: 'Carpet and rug mills',
    1590: 'Textile product mills, except carpets and rugs',
    1670: 'Knitting mills',
    1680: 'Cut and sew apparel manufacturing',
    1690: 'Apparel accessories and other apparel manufacturing',
    1770: 'Footwear manufacturing',
    1790: 'Leather tanning and products, except footwear manufacturing',
    1870: 'Pulp, paper, and paperboard mills',
    1880: 'Paperboard containers and boxes',
    1890: 'Miscellaneous paper and pulp products',
    1990: 'Printing and related support activities',
    2070: 'Petroleum refining',
    2090: 'Miscellaneous petroleum and coal products',
    2170: 'Resin, synthetic rubber and fibers, and filaments manufacturing',
    2180: 'Agricultural chemical manufacturing',
    2190: 'Pharmaceutical and medicine manufacturing',
    2270: 'Paint, coating, and adhesive manufacturing B46',
    2280: 'Soap, cleaning compound, and cosmetics manufacturing',
    2290: 'Industrial and miscellaneous chemicals',
    2370: 'Plastics product manufacturing',
    2380: 'Tire manufacturing',
    2390: 'Rubber products, except tires, manufacturing',
    2470: 'Pottery, ceramics, and related products manufacturing',
    2480: 'Structural clay product manufacturing',
    2490: 'Glass and glass product manufacturing',
    2570: 'Cement, concrete, lime, and gypsum product manufacturing',
    2590: 'Miscellaneous nonmetallic mineral product manufacturing',
    2670: 'Iron and steel mills and steel product manufacturing',
    2680: 'Aluminum production and processing',
    2690: 'Nonferrous metal, except aluminum, production and processing',
    2770: 'Foundries',
    2780: 'Metal forgings and stampings',
    2790: 'Cutlery and hand tool manufacturing',
    2870: 'Structural metals, and tank and shipping container manufacturing',
    2880: 'Machine shops; turned product; screw, nut and bolt manufacturing',
    2890: 'Coating, engraving, heat treating and allied activities',
    2970: 'Ordnance 332992 to',
    2980: 'Miscellaneous fabricated metal products manufacturing',
    2990: 'Not specified metal industries Part of',
    3070: 'Agricultural implement manufacturing',
    3080: 'Construction, mining and oil field machinery manufacturing',
    3090: 'Commercial and service industry machinery manufacturing',
    3170: 'Metalworking machinery manufacturing',
    3180: 'Engines, turbines, and power transmission equipment manufacturing',
    3190: 'Machinery manufacturing, n.e.c.',
    3290: 'Not specified machinery manufacturing Part of',
    3360: 'Computer and peripheral equipment manufacturing',
    3370: 'Communications, audio, and video equipment manufacturing',
    3380: 'Navigational, measuring, electromedical, and control instruments manufacturing',
    3390: 'Electronic component and product manufacturing, n.e.c.',
    3470: 'Household appliance manufacturing',
    3490: 'Electrical lighting, equipment, and supplies manufacturing, n.e.c.',
    3570: 'Motor vehicles and motor vehicle equipment manufacturing',
    3580: 'Aircraft and parts manufacturing 336411 to',
    3590: 'Aerospace products and parts manufacturing',
    3670: 'Railroad rolling stock manufacturing',
    3680: 'Ship and boat building',
    3690: 'Other transportation equipment manufacturing',
    3770: 'Sawmills and wood preservation',
    3780: 'Veneer, plywood, and engineered wood products',
    3790: 'Prefabricated wood buildings and mobile homes',
    3870: 'Miscellaneous wood products',
    3890: 'Furniture and related product manufacturing',
    3960: 'Medical equipment and supplies manufacturing',
    3970: 'Toys, amusement, and sporting goods manufacturing',
    3980: 'Miscellaneous manufacturing, n.e.c.',
    3990: 'Not specified manufacturing industries Part of',
    4070: 'Motor vehicles, parts and supplies, merchant wholesalers',
    4080: 'Furniture and home furnishing, merchant wholesalers',
    4090: 'Lumber and other construction materials, merchant wholesalers',
    4170: 'Professional and commercial equipment and supplies, merchant wholesalers',
    4180: 'Metals and minerals, except petroleum, merchant wholesalers',
    4190: 'Electrical goods, merchant wholesalers',
    4260: 'Hardware, plumbing and heating equipment, and supplies, merchant wholesalers',
    4270: 'Machinery, equipment, and supplies, merchant wholesalers',
    4280: 'Recyclable material, merchant wholesalers',
    4290: 'Miscellaneous durable goods, merchant wholesalers',
    4370: 'Paper and paper products, merchant wholesalers',
    4380: 'Drugs, sundries, and chemical and allied products, merchant wholesalers',
    4390: 'Apparel, fabrics, and notions, merchant wholesalers',
    4470: 'Groceries and related products, merchant wholesalers',
    4480: 'Farm product raw materials, merchant wholesalers',
    4490: 'Petroleum and petroleum products, merchant wholesalers',
    4560: 'Alcoholic beverages, merchant wholesalers',
    4570: 'Farm supplies, merchant wholesalers',
    4580: 'Miscellaneous nondurable goods, merchant wholesalers',
    4585: 'Wholesale electronic markets, agents and brokers',
    4590: 'Not specified wholesale trade Part of',
    4670: 'Automobile dealers',
    4680: 'Other motor vehicle dealers',
    4690: 'Auto parts, accessories, and tire stores',
    4770: 'Furniture and home furnishings stores',
    4780: 'Household appliance stores',
    4790: 'Radio, TV, and computer stores',
    4870: 'Building material and supplies dealers',
    4880: 'Hardware stores',
    4890: 'Lawn and garden equipment and supplies stores',
    4970: 'Grocery stores',
    4980: 'Specialty food stores',
    4990: 'Beer, wine, and liquor stores',
    5070: 'Pharmacies and drug stores',
    5080: 'Health and personal care, except drug, stores',
    5090: 'Gasoline stations',
    5170: 'Clothing and accessories, except shoe, stores',
    5180: 'Shoe stores',
    5190: 'Jewelry, luggage, and leather goods stores',
    5270: 'Sporting goods, camera, and hobby and toy stores',
    5280: 'Sewing, needlework, and piece goods stores',
    5290: 'Music stores',
    5370: 'Book stores and news dealers',
    5380: 'Department stores and discount stores',
    5390: 'Miscellaneous general merchandise stores',
    5470: 'Retail florists',
    5480: 'Office supplies and stationery stores',
    5490: 'Used merchandise stores',
    5570: 'Gift, novelty, and souvenir shops',
    5580: 'Miscellaneous retail stores',
    5590: 'Electronic shopping',
    5591: 'Electronic auctions',
    5592: 'Mail order houses',
    5670: 'Vending machine operators',
    5680: 'Fuel dealers',
    5690: 'Other direct selling establishments',
    5790: 'Not specified retail trade Part of',
    6070: 'Air transportation',
    6080: 'Rail transportation',
    6090: 'Water transportation',
    6170: 'Truck transportation',
    6180: 'Bus service and urban transit',
    6190: 'Taxi and limousine service',
    6270: 'Pipeline transportation',
    6280: 'Scenic and sightseeing transportation',
    6290: 'Services incidental to transportation',
    6370: 'Postal Service',
    6380: 'Couriers and messengers',
    6390: 'Warehousing and storage',
    6470: 'Newspaper publishers',
    6480: 'Publishing, except newspapers and software',
    6490: 'Software publishing',
    6570: 'Motion pictures and video industries',
    6590: 'Sound recording industries',
    6670: 'Radio and television broadcasting and cable',
    6672: '(Internet publishing and broadcasting and web search portals)', #this code replaced 6675, 6692
    6675: '(Internet publishing and broadcasting)',
    6680: 'Wired telecommunications carriers',
    6690: 'Other telecommunications services',
    6692: '(Internet service providers)',
    6695: 'Data processing, hosting, and related services',
    6770: 'Libraries and archives',
    6780: 'Other information services',
    6870: 'Banking and related activities 521,52211,',
    6880: 'Savings institutions, including credit unions',
    6890: 'Non-depository credit and related activities',
    6970: 'Securities, commodities, funds, trusts, and other financial investments',
    6990: 'Insurance carriers and related activities',
    7070: 'Real estate',
    7080: 'Automotive equipment rental and leasing',
    7170: 'Video tape and disk rental',
    7180: 'Other consumer goods rental',
    7190: 'Commercial, industrial, and other intangible assets rental and leasing',
    7270: 'Legal services',
    7280: 'Accounting, tax preparation, bookkeeping, and payroll services',
    7290: 'Architectural, engineering, and related services',
    7370: 'Specialized design services',
    7380: 'Computer systems design and related services',
    7390: 'Management, scientific, and technical consulting services',
    7460: 'Scientific research and development services',
    7470: 'Advertising and related services',
    7480: 'Veterinary services',
    7490: 'Other professional, scientific, and technical services',
    7570: 'Management of companies and enterprises',
    7580: 'Employment services',
    7590: 'Business support services',
    7670: 'Travel arrangements and reservation services',
    7680: 'Investigation and security services',
    7690: 'Services to buildings and dwellings',
    7770: 'Landscaping services',
    7780: 'Other administrative and other support services',
    7790: 'Waste management and remediation services',
    7860: 'Elementary and secondary schools',
    7870: 'Colleges and universities, including junior colleges',
    7880: 'Business, technical, and trade schools and training',
    7890: 'Other schools, instruction, and educational services',
    7970: 'Offices of physicians',
    7980: 'Offices of dentists',
    7990: 'Offices of chiropractors',
    8070: 'Offices of optometrists',
    8080: 'Offices of other health practitioners',
    8090: 'Outpatient care centers',
    8170: 'Home health care services',
    8180: 'Other health care services',
    8190: 'Hospitals',
    8270: 'Nursing care facilities',
    8290: 'Residential care facilities, without nursing',
    8370: 'Individual and family services',
    8380: 'Community food and housing, and emergency services',
    8390: 'Vocational rehabilitation services',
    8470: 'Child day care services',
    8560: 'Independent artists, performing arts, spectator sports, and related industries',
    8570: 'Museums, art galleries, historical sites, and similar institutions',
    8580: 'Bowling centers',
    8590: 'Other amusement, gambling, and recreation industries',
    8660: 'Traveler accommodation',
    8670: 'Recreational vehicle parks and camps, and rooming and boarding houses',
    8680: 'Restaurants and other food services',
    8690: 'Drinking places, alcoholic beverages',
    8770: 'Automotive repair and maintenance',
    8780: 'Car washes',
    8790: 'Electronic and precision equipment repair and maintenance',
    8870: 'Commercial and industrial machinery and equipment repair and maintenance',
    8880: 'Personal and household goods repair and maintenance',
    8890: 'Footwear and leather goods repair',
    8970: 'Barber shops',
    8980: 'Beauty salons',
    8990: 'Nail salons and other personal care services',
    9070: 'Dry cleaning and laundry services',
    9080: 'Funeral homes, cemeteries, and crematories',
    9090: 'Other personal services',
    9160: 'Religious organizations',
    9170: 'Civic, social, advocacy organizations, and grant making and giving services',
    9180: 'Labor unions',
    9190: 'Business, professional, political, and similar organizations',
    9290: 'Private households',
    9370: 'Executive offices and legislative bodies',
    9380: 'Public finance activities',
    9390: 'Other general government and support',
    9470: 'Justice, public order, and safety activities',
    9480: 'Administration of human resource programs',
    9490: 'Administration of environmental quality and housing programs',
    9570: 'Administration of economic programs and space research',
    9590: 'National security and international affairs',
    9890: 'Armed Forces'
}

states = _GESTCEN_DICT.values()

_PESEX_DICT = { 1: 'M', 2: 'F' }

_PEEDUCA_DICT = {
    -3: 'REFUSED',
    -2: "DON'T KNOW",
    -1: 'BLANK',
    31: 'LESS THAN 1ST GRADE',
    32: '1ST, 2ND, 3RD OR 4TH GRADE',
    33: '5TH OR 6TH GRADE',
    34: '7TH OR 8TH GRADE',
    35: '9TH GRADE',
    36: '10TH GRADE',
    37: '11TH GRADE',
    38: '12TH GRADE NO DIPLOMA',
    39: 'HIGH SCHOOL GRAD-DIPLOMA OR EQUIV (GED)',
    40: 'SOME COLLEGE BUT NO DEGREE',
    41: 'ASSOCIATE DEGREE-OCCUPATIONAL/VOCATIONAL',
    42: 'ASSOCIATE DEGREE-ACADEMIC PROGRAM',
    43: "BACHELOR'S DEGREE",       #(EX: BA, AB, BS)
    44: "MASTER'S DEGREE",         #(EX: MA, MS, MEng, MEd, MSW)
    45: 'PROFESSIONAL SCHOOL DEG', #(EX: MD, DDS, DVM)'
    46: 'DOCTORATE DEGREE'         #(EX: PhD, EdD)
}

education_levels = _PEEDUCA_DICT.values()

_PEDIPGED_DICT = {-1: 'N/A', 1: 'HIGH SCHOOL GRAD', 2: 'GED/EQUIVALENT'}

_PESCI1_DICT = {-1: 'BLANK', 1: 'Y', 2: 'N'}
_PESC2A3_DICT = _PESCI1_DICT.copy()
_PESC2A4_DICT = _PESCI1_DICT.copy()
_PEPERSCR_DICT = _PESCI1_DICT.copy()
_PEHOME_DICT = _PESCI1_DICT.copy()
_PESCHL_DICT = _PESCI1_DICT.copy()
_PEWRKA_DICT = _PESCI1_DICT.copy()
_PEDESK_DICT = _PESCI1_DICT.copy()
_PELAPT_DICT = _PESCI1_DICT.copy()
_PETABL_DICT = _PESCI1_DICT.copy()
_PECELL_DICT = _PESCI1_DICT.copy()
_PEGAME_DICT = _PESCI1_DICT.copy()
_PEPR3A5_DICT = _PESCI1_DICT.copy()
_PEPRIM7_DICT = _PESCI1_DICT.copy()
_PEPRIM10_DICT = _PESCI1_DICT.copy()
_PEPRM15X_DICT = {-1: 'BLANK', 0: '-', 1: 'Y', 2: 'N'}

_HESCI5_DICT = _PESCI1_DICT.copy()

_HESCI8_DICT = {
    1: "Don't need it, not interested",
    2: 'Too expensive',
    3: 'Can use it somewhere else',
    4: 'Not available in area',
    5: 'No computer or computer inadequate',
    6: 'Privacy or security concerns',
    7: 'Other reason'
}

_VARIABLE_INDICES_BASIC_SURVEY = {
    'HRHHID':    ( 0,  15), #HOUSEHOLD IDENTIFIER (part 1)
    'HRMONTH':   (15,  17), #MONTH OF INTERVIEW
    'HRYEAR4':   (17,  21), #YEAR OF INTERVIEW
    'HURESPLI':  (21,  23),
    'HUFINAL':   (23,  26), #FINAL OUTCOME CODE
    'HUSPNISH':  (26,  28),
    'HETENURE':  (28,  30),
    'HEHOUSUT':  (30,  32),
    'HETELHHD':  (32,  34),
    'HETELAVL':  (34,  36),
    'HEPHONEO':  (36,  38),
    'HEFAMINC':  (38,  40),
    'HUTYPEA':   (40,  42),
    'HUTYPB':    (42,  44),
    'HUTYPC':    (44,  46),
    'HWHHWGT':   (46,  56),
    'HRINTSTA':  (56,  58), #INTERVIEW STATUS
    'HRNUMHOU':  (58,  60), #TOTAL NUMBER OF PERSONS LIVING IN THE HOUSEHOLD (HOUSEHOLD MEMBERS).
    'HRHTYPE':   (60,  62),
    'HRMIS':     (62,  64),
    'HUINTTYP':  (64,  66),
    'HUPRSCNT':  (66,  68),
    'HRLONGLK':  (68,  70),
    'HRHHID2':   (70,  75), #HOUSEHOLD IDENTIFIER (part 2)
    #FILLER      (75,  78)
    'HUBUS':     (78,  80),
    'HUBUSL1':   (80,  82),
    'HUBUSL2':   (82,  84),
    'HUBUSL3':   (84,  86),
    'HUBUSL4':   (86,  88),
    'GEREG':     (88,  90),
    'GESTCEN':   (90,  92), #CENSUS STATE CODE
    'GESTFIPS':  (92,  94),
    #FILLER      (94,  95)
    'GTCBSA':    (95, 100),
    'GTCO':     (100, 103),
    'GTCBSAST': (103, 104),
    'GTMETSTA': (104, 105),
    'GTINDVPC': (105, 106),
    'GTCBSASZ': (106, 107),
    'GTCSA':    (107, 110),
    #FILLER     (110, 115) ... PROLDRRP(113:115) eliminated 2005/02
    'PUPELIG':  (115, 117),
    'PERRP':    (117, 119),
    'PEPARENT': (119, 121),
    'PEAGE':    (121, 123), #PERSON'S AGE AS OF THE END OF SURVEY WEEK
    'PRTFAGE':  (123, 124),
    'PEMARITL': (124, 126),
    'PESPOUSE': (126, 128),
    'PESEX':    (128, 130), #PERSON'S SEX
    'PEAFEVER': (130, 132),
    #FILLER     (132, 134)
    'PEAFNOW':  (134, 136),
    'PEEDUCA':  (136, 138), #HEIGHEST LEVEL OF SCHOOL COMPLETED OR DEGREE RECEIVED
    'PTDTRACE': (138, 140),
    'PRDTHSP':  (140, 142),
    'PUCHINHH': (142, 144),
    #FILLER     (144, 146)
    'PULINENO': (146, 148),
    #FILLER     (148, 150)
    'PRFAMNUM': (150, 152),
    'PRFAMREL': (152, 154),
    'PRFAMTYP': (154, 156),
    'PEHSPNON': (156, 158),
    'PRMARSTA': (158, 160),
    'PRPERTYP': (160, 162),
    'PENATVTY': (162, 165),
    'PEMNTVTY': (165, 168),
    'PEFNTVTY': (168, 171),
    'PRCITSHP': (171, 173),
    'PRCITFLG': (173, 175),
    'PRINUSYR': (175, 177), #beware monkey business with values
    'PUSLFPRX': (177, 179),
    'PEMLR':    (179, 181),
    'PUWK':     (181, 183),
    'PUBUS1':   (183, 185),
    'PUBUS2OT': (185, 187),
    'PUBUSCK1': (187, 189),
    'PUBUSCK2': (189, 191),
    'PUBUSCK3': (191, 193),
    'PUBUSCK4': (193, 195),
    'PURETOT':  (195, 197),
    'PUDIS':    (197, 199),
    'PERET1':   (199, 201),
    'PUDIS1':   (201, 203),
    'PUDIS2':   (203, 205),
    'PUABSOT':  (205, 207),
    'PULAY':    (207, 209),
    'PEABSRSN': (209, 211),
    'PEABSPDO': (211, 213),
    'PEMJOT':   (213, 215),
    'PEMJNUM':  (215, 217),
    'PEHRUSL1': (217, 219),
    'PEHRUSL2': (219, 221),
    'PEHRFTPT': (221, 223),
    'PEHRUSLT': (223, 226),
    'PEHRWANT': (226, 228),
    'PEHRRSN1': (228, 230),
    'PEHRRSN2': (230, 232),
    'PEHRRSN3': (232, 234),
    'PUHROFF1': (234, 236),
    'PUHROFF2': (236, 238),
    'PUHROT1':  (238, 240),
    'PUHROT2':  (240, 242),
    'PEHRACT1': (242, 244),
    'PEHRACT2': (244, 246),
    'PEHRACTT': (246, 249),
    'PEHRAVL':  (249, 251),
    #FILLER     (251, 256)
    'PUHRCK1':  (256, 258),
    'PUHRCK2':  (258, 260),
    'PUHRCK3':  (260, 262),
    'PUHRCK4':  (262, 264),
    'PUHRCK5':  (264, 266),
    'PUHRCK6':  (266, 268),
    'PUHRCK7':  (268, 270),
    'PUHRCK12': (270, 272),
    'PULAYDT':  (272, 274),
    'PULAY6M':  (274, 276),
    'PELAYAVL': (276, 278),
    'PULAYAVR': (278, 280),
    'PELAYLK':  (280, 282),
    'PELAYDUR': (282, 285),
    'PELAYFTO': (285, 287),
    'PULAYCK1': (287, 289),
    'PULAYCK2': (289, 291),
    'PULAYCK3': (291, 293),
    'PULK':     (293, 295),
    'PELKM1':   (295, 297),
    'PULKM2':   (297, 299),
    'PULKM3':   (299, 301),
    'PULKM4':   (301, 303),
    'PULKM5':   (303, 305),
    'PULKM6':   (305, 307),
    'PULKDK1':  (307, 309),
    'PULKDK2':  (309, 311),
    'PULKDK3':  (311, 313),
    'PULKDK4':  (313, 315),
    'PULKDK5':  (315, 317),
    'PULKDK6':  (317, 319),
    'PULKPS1':  (319, 321),
    'PULKPS2':  (321, 323),
    'PULKPS3':  (323, 325),
    'PULKPS4':  (325, 327),
    'PULKPS5':  (327, 329),
    'PULKPS6':  (329, 331),
    'PELKAVL':  (331, 333),
    'PULKAVR':  (333, 335),
    'PELKLL1O': (335, 337),
    'PELKLL2O': (337, 339),
    'PELKLWO':  (339, 341),
    'PELKDUR':  (341, 344),
    'PELKFTO':  (344, 346),
    'PEDWWNTO': (346, 348),
    'PEDWRSN':  (348, 350),
    'PEDWLKO':  (350, 352),
    'PEDWWK':   (352, 354),
    'PEDW4WK':  (354, 356),
    'PEDWLKWK': (356, 358),
    'PEDWAVL':  (358, 360),
    'PEDWAVR':  (360, 362),
    'PUDWCK1':  (362, 364),
    'PUDWCK2':  (364, 366),
    'PUDWCK3':  (366, 368),
    'PUDWCK4':  (368, 370),
    'PUDWCK5':  (370, 372),
    'PEJHWKO':  (372, 374),
    'PUJHDP1O': (374, 376),
    'PEJHRSN':  (376, 378),
    'PEJHWANT': (378, 380),
    'PUJHCK1':  (380, 382),
    'PUJHCK2':  (382, 384),
    'PRABSREA': (384, 386),
    'PRCIVLF':  (386, 388),
    'PRDISC':   (388, 390),
    'PREMPHRS': (390, 392),
    'PREMPNOT': (392, 394),
    'PREXPLF':  (394, 396),
    'PRFTLF':   (396, 398),
    'PRHRUSL':  (398, 400),
    'PRJOBSEA': (400, 402),
    'PRPTHRS':  (402, 404),
    'PRPTREA':  (404, 406),
    'PRUNEDUR': (406, 409),
    #FILLER     (409, 411)
    'PRUNTYPE': (411, 413),
    'PRWKSCH':  (413, 415),
    'PRWKSTAT': (415, 417),
    'PRWNTJOB': (417, 419),
    'PUJHCK3':  (419, 421),
    'PUJHCK4':  (421, 423),
    'PUJHCK5':  (423, 425),
    'PUIODP1':  (425, 427),
    'PUIODP2':  (427, 429),
    'PUIODP3':  (429, 431),
    'PEIO1COW': (431, 433),
    'PUIO1MFG': (433, 435),
    #PADDING    (435, 441)
    'PEIO2COW': (441, 443),
    'PUIO2MFG': (443, 445),
    #PADDING    (445, 451)
    'PUIOCK1':  (451, 453),
    'PUIOCK2':  (453, 455),
    'PUIOCK3':  (455, 457),
    'PRIOELG':  (457, 459),
    'PRAGNA':   (459, 461),
    'PRCOW1':   (461, 463),
    'PRCOW2':   (463, 465),
    'PRCOWPG':  (465, 467),
    'PRDTCOW1': (467, 469),
    'PRDTCOW2': (469, 471),
    'PRDTIND1': (471, 473),
    'PRDTIND2': (473, 475),
    'PRDTOCC1': (475, 477),
    'PRDTOCC2': (477, 479),
    'PREMP':    (479, 481),
    'PRMJIND1': (481, 483),
    'PRMJIND2': (483, 485),
    'PRMJOCC1': (485, 487),
    'PRMJOCC2': (487, 489),
    'PRMJOCGR': (489, 491),
    'PRNAGPWS': (491, 493),
    'PRNAGWS':  (493, 495),
    'PRSJMJ':   (495, 497),
    'PRERELG':  (497, 499),
    'PEERNUOT': (499, 501),
    'PEERNPER': (501, 503),
    'PEERNRT':  (503, 505),
    'PEERNHRY': (505, 507),
    'PUERNH1C': (507, 511),
    'PEERNH2':  (511, 515),
    'PEERNH1O': (515, 519),
    'PRERNHLY': (519, 523),
    'PTHR':     (523, 524),
    'PEERNHRO': (524, 526),
    'PRERNWA':  (526, 534),
    'PTWK':     (534, 535),
    #FILLER     (535, 539)
    'PEERN':    (539, 547),
    'PUERN2':   (547, 555),
    'PTOT':     (555, 556),
    #FILLER     (556, 558)
    'PEERNWKP': (558, 560),
    'PEERNLAB': (560, 562),
    'PEERNCOV': (562, 564),
    'PENLFJH':  (564, 566),
    'PENLFRET': (566, 568),
    'PENLFACT': (568, 570),
    'PUNLFCK1': (570, 572),
    'PUNLFCK2': (572, 574),
    'PESCHENR': (574, 576),
    'PESCHFT':  (576, 578),
    'PESCHLVL': (578, 580),
    'PRNLFSCH': (580, 582),
    'PWFMWGT':  (582, 592),
    'PWLGWGT':  (592, 602),
    'PWORWGT':  (602, 612),
    'PWSSWGT':  (612, 622),
    'PWVETWGT': (622, 632),
    'PRCHLD':   (632, 634),
    'PRNMCHLD': (634, 636),
    #FILLER     (636, 638)
    'PRWERNAL': (638, 640),
    'PRHERNAL': (640, 642),
    'HXTENURE': (642, 644),
    'HXHOUSUT': (644, 646),
    'HXTELHHD': (646, 648),
    'HXTELAVL': (648, 650),
    'HXPHONEO': (650, 652),
    'PXINUSYR': (652, 654),
    'PXRRP':    (654, 656),
    'PXPARENT': (656, 658),
    'PXAGE':    (658, 660),
    'PXMARITL': (660, 662),
    'PXSPOUSE': (662, 664),
    'PXSEX':    (664, 666),
    'PXAFWHN1': (666, 668),
    'PXAFNOW':  (668, 670),
    'PXEDUCA':  (670, 672),
    'PXRACE1':  (672, 674),
    'PXNATVTY': (674, 676),
    'PXMNTVTY': (676, 678),
    'PXFNTVTY': (678, 680),
    #FILLER     (680, 682)
    'PXHSPNON': (682, 684),
    'PXMLR':    (684, 686),
    'PXRET1':   (686, 688),
    'PXABSRSN': (688, 690),
    'PXABSPDO': (690, 692),
    'PXMJOT':   (692, 694),
    'PXMJNUM':  (694, 696),
    'PXHRUSL1': (696, 698),
    'PXHRUSL2': (698, 700),
    'PXHRFTPT': (700, 702),
    'PXHRUSLT': (702, 704),
    'PXHRWANT': (704, 706),
    'PXHRRSN1': (706, 708),
    'PXHRRSN2': (708, 710),
    'PXHRACT1': (710, 712),
    'PXHRACT2': (712, 714),
    'PXHRACTT': (714, 716),
    'PXHRRSN3': (716, 718),
    'PXHRAVL':  (718, 720),
    'PXLAYAVL': (720, 722),
    'PXLAYLK':  (722, 724),
    'PXLAYDUR': (724, 726),
    'PXLAYFTO': (726, 728),
    'PXLKM1':   (728, 730),
    'PXLKAVL':  (730, 732),
    'PXLKLL1O': (732, 734),
    'PXLKLL2O': (734, 736),
    'PXLKLWO':  (736, 738),
    'PXLKDUR':  (738, 740),
    'PXLKFTO':  (740, 742),
    'PXDWWNTO': (742, 744),
    'PXDWRSN':  (744, 746),
    'PXDWLKO':  (746, 748),
    'PXDWWK':   (748, 750),
    'PXDW4WK':  (750, 752),
    'PXDWLKWK': (752, 754),
    'PXDWAVL':  (754, 756),
    'PXDWAVR':  (756, 758),
    'PXJHWKO':  (758, 760),
    'PXJHRSN':  (760, 762),
    'PXJHWANT': (762, 764),
    'PXIO1COW': (764, 766),
    'PXIO1ICD': (766, 768),
    'PXIO1OCD': (768, 770),
    'PXIO2COW': (770, 772),
    'PXIO2ICD': (772, 774),
    'PXIO2OCD': (774, 776),
    'PXERNUOT': (776, 778),
    'PXERNPER': (778, 780),
    'PXERNH1O': (780, 782),
    'PXERNHRO': (782, 784),
    'PXERN':    (784, 786),
    #FILLER     (786, 790)
    'PXERNWKP': (790, 792),
    'PXERNRT':  (792, 794),
    'PXERNHRY': (794, 796),
    'PXERNH2':  (796, 798),
    'PXERNLAB': (798, 800),
    'PXERNCOV': (800, 802),
    'PXNLFJH':  (802, 804),
    'PXNLFRET': (804, 806),
    'PXNLFACT': (806, 808),
    'PXSCHENR': (808, 810),
    'PXSCHFT':  (810, 812),
    'PXSCHLVL': (812, 814),
    'QSTNUM':   (814, 819),
    'OCCURNUM': (819, 821),
    'PEDIPGED': (821, 823), #HOW DID...GET...'S HIGH SCHOOL DIPLOMA?
    'PEHGCOMP': (823, 825),
    'PECYC':    (825, 827),
    'PEGRPROF': (827, 829),
    'PEGR6COR': (829, 831),
    'PEMS123':  (831, 833),
    'PXDIPGED': (833, 835),
    'PXHGCOMP': (835, 837),
    'PXCYC':    (837, 839),
    'PXGRPROF': (839, 841),
    'PXGR6COR': (841, 843),
    'PXMS123':  (843, 845),
    'PWCMPWGT': (845, 855),
    'PEIO1ICD': (855, 859),
    'PEIO1OCD': (859, 863),
    'PEIO2ICD': (863, 867),
    'PEIO2OCD': (867, 871),
    'PRIMIND1': (871, 873),
    'PRIMIND2': (873, 875),
    'PEAFWHN1': (875, 877),
    'PEAFWHN2': (877, 879),
    'PEAFWHN3': (879, 881),
    'PEAFWHN4': (881, 883),
    'PXAFEVER': (883, 885),
    'PELNDAD':  (885, 887),
    'PELNMOM':  (887, 889),
    'PEDADTYP': (889, 891),
    'PEMOMTYP': (891, 893),
    'PECOHAB':  (893, 895),
    'PXLNDAD':  (895, 897),
    'PXLNMOM':  (897, 899),
    'PXDADTYP': (899, 901),
    'PXMOMTYP': (901, 903),
    'PXCOHAB':  (903, 905),
    'PEDISEAR': (905, 907),
    'PEDISEYE': (907, 909),
    'PEDISREM': (909, 911),
    'PEDISPHY': (911, 913),
    'PEDISDRS': (913, 915),
    'PEDISOUT': (915, 917),
    'PRDISFLG': (917, 919),
    'PXDISEAR': (919, 921),
    'PXDISEYE': (921, 923),
    'PXDISREM': (923, 925),
    'PXDISPHY': (925, 927),
    'PXDISDRS': (927, 929),
    'PXDISOUT': (929, 931),
    'HXFAMINC': (931, 933),
    #FILLER     (933, 950)
}

_VARIABLE_INDICES_INTERNET_SURVEY = {
    'PESCI1':    (950,  952), #Do you use a cellular phone or smartphone?
    'PESC2A1':   (952,  954),
    'PESC2A2':   (954,  956),
    'PESC2A3':   (956,  958), #Do you use a mobile phone to browse the web?
    'PESC2A4':   (958,  960), #Do you use a mobile phone to e-mail?
    'PESC2A5':   (960,  962),
    'PESC2A6':   (962,  964),
    'PESC2A7':   (964,  966),
    'PESC2A8':   (966,  968),
    'PESC2A9':   (968,  970),
    'PESC2A10':  (970,  972),
    'HESCI3':    (972,  974),
    'HTSCI4':    (974,  978),
    'HESCI5':    (978,  980), #Does anyone in this household use the Internet from home?
    'HESCI6':    (980,  982), #Has anyone in this household ever used the Internet from this home?
    'HESCI71':   (982,  984), #At home, does anyone in this household access the Internet using dial-up service?
    'HESCI72':   (984,  986), #At home, does anyone in this household access the Internet using DSL service?
    'HESCI73':   (986,  988), #At home, does anyone in this household access the Internet using cable modem service?
    'HESCI74':   (988,  990), #At home, does anyone in this household access the Internet using fiber-optic service?
    'HESCI75':   (990,  992), #At home, does anyone in this household access the Internet using mobile broadband plan?
    'HESCI76':   (992,  994), #At home, does anyone in this household access the Internet using satellite service?
    'HESCI77':   (994,  996), #At home, does anyone in this household access the Internet using some other service?
    'HESCI8':    (996,  998), #What is the main reason that you do not have high-speed Internet access at home?
    'HESCI9':    (998, 1000),
    'HESCI101': (1000, 1002),
    'HESCI102': (1002, 1004),
    'HESCI103': (1004, 1006),
    'HESCI104': (1006, 1008),
    'HESCI11':  (1008, 1010),
    'HESCI121': (1010, 1012),
    'HESCI122': (1012, 1014),
    'HESCI123': (1014, 1016),
    'HESCI124': (1016, 1018),
    'HTSCI13':  (1018, 1021),
    'HTSCI14':  (1021, 1023),
    'HESCI15':  (1023, 1025),
    'HESCI16':  (1025, 1027),
    'HESCI17':  (1027, 1029),
    'HESCI18':  (1029, 1031),
    'HESCI19':  (1031, 1033),
    'HESCI20':  (1033, 1035),
    'HESCI21':  (1035, 1037),
    'PEPERSCR': (1037, 1039),
    'PEHOME':   (1039, 1041),
    'PESCHL':   (1041, 1043),
    'PEWRKA':   (1043, 1045),
    'PELIBR':   (1045, 1047),
    'PECOMM':   (1047, 1049),
    'PECAFE':   (1049, 1051),
    'PEELHS':   (1051, 1053),
    'PEDESK':   (1053, 1055),
    'PELAPT':   (1055, 1057),
    'PETABL':   (1057, 1059),
    'PECELL':   (1059, 1061),
    'PEGAME':   (1061, 1063),
    'PETVBA':   (1063, 1065),
    'PEPRIM1':  (1065, 1067),
    'PTPRIM2':  (1067, 1069),
    'PEPR3A1':  (1069, 1071),
    'PEPR3A2':  (1071, 1073),
    'PEPR3A3':  (1073, 1075),
    'PEPR3A4':  (1075, 1077),
    'PEPR3A5':  (1077, 1079),
    'PEPR3A6':  (1079, 1081),
    'PEPR3A7':  (1081, 1083),
    'PEPR3A8':  (1083, 1085),
    'PEPR3A9':  (1085, 1087),
    'PEPR3A10': (1087, 1089),
    'PEPR3A11': (1089, 1091),
    'PEPRIM5':  (1091, 1093),
    'PEPRIM6':  (1093, 1095),
    'PEPRIM7':  (1095, 1097),
    'PEPRIM8':  (1097, 1099),
    'PEPRIM9':  (1099, 1101),
    'PEPRIM10': (1101, 1103),
    'PEPRIM11': (1103, 1105),
    'PEPRIM12': (1105, 1107),
    'PEPRM141': (1107, 1109),
    'PEPRM142': (1109, 1111),
    'PEPRM143': (1111, 1113),
    'PEPRM144': (1113, 1115),
    'PEPRM145': (1115, 1117),
    'PEPRM146': (1117, 1119),
    'PEPRM147': (1119, 1121),
    'PEPRM151': (1121, 1123),
    'PEPRM152': (1123, 1125),
    'PEPRM153': (1125, 1127),
    'PEPRM154': (1127, 1129),
    'PEPRM155': (1129, 1131),
    'PEPRM156': (1131, 1133),
    'PEPRM15B': (1133, 1135),
    'PEPRM161': (1135, 1137),
    'PEPRM162': (1137, 1139),
    'PEPRM163': (1139, 1141),
    'PEPRM164': (1141, 1143),
    'PEPRM16B': (1143, 1145),
    'PRHRESP':  (1145, 1147),
    'PXSCI1':   (1147, 1149),
    'PXSC2A':   (1149, 1151),
    'HXSCI3':   (1151, 1153),
    'HXSCI4':   (1153, 1155),
    'HXSCI5':   (1155, 1157),
    'HXSCI6':   (1157, 1159),
    'HXSCI7':   (1159, 1161),
    'HXSCI8':   (1161, 1163),
    'HXSCI9':   (1163, 1165),
    'HXSCI10':  (1165, 1167),
    'HXSCI11':  (1167, 1169),
    'HXSCI12':  (1169, 1171),
    'HXSCI13':  (1171, 1173),
    'HXSCI14':  (1173, 1175),
    'HXSCI15':  (1175, 1177),
    'HXSCI16':  (1177, 1179),
    'HXSCI17':  (1179, 1181),
    'HXSCI18':  (1181, 1183),
    'HXSCI19':  (1183, 1185),
    'HXSCI20':  (1185, 1187),
    'HXSCI21':  (1187, 1189),
    'PXHOME':   (1189, 1191),
    'PXSCHL':   (1191, 1193),
    'PXWRKA':   (1193, 1195),
    'PXLIBR':   (1195, 1197),
    'PXCOMM':   (1197, 1199),
    'PXCAFE':   (1199, 1201),
    'PXELHS':   (1201, 1203),
    'PXDESK':   (1203, 1205),
    'PXLAPT':   (1205, 1207),
    'PXTABL':   (1207, 1209),
    'PXCELL':   (1209, 1211),
    'PXGAME':   (1211, 1213),
    'PXTVBA':   (1213, 1215),
    'PXPRIM1':  (1215, 1217),
    'PXPRIM2':  (1217, 1219),
    'PXPR3A':   (1219, 1221),
    'PXPRIM5':  (1221, 1223),
    'PXPRIM6':  (1223, 1225),
    'PXPRIM7':  (1225, 1227),
    'PXPRIM8':  (1227, 1229),
    'PXPRIM9':  (1229, 1231),
    'PXPRIM10': (1231, 1233),
    'PXPRIM11': (1233, 1235),
    'PXPRIM12': (1235, 1237),
    'PXPRM14':  (1237, 1239),
    'PXPRM15':  (1239, 1241),
    'PXPRM15B': (1241, 1243),
    'PXPRM16':  (1243, 1245),
    'PXPRM16B': (1245, 1247),
    'PXPERSCR': (1247, 1249),
    'PWPRMWGT': (1249, 1259)
}

class DataError(ValueError):
    def __init__(self, household_id, message):
        super(DataError, self).__init__(message)
        self._hid = household_id
    
    def __str__(self):
        return '[%20d] %s' % (self._hid, self.message)


class CurrentPopulationSurveyBasicRecord(object):
    '''Current Population Survey: basic interview, single response.'''
    
    RECORD_LENGTH = 950 #length of raw data
    
    def __init__(self, raw_data, indices=None):
        if len(raw_data) < CurrentPopulationSurveyBasicRecord.RECORD_LENGTH:
            raise Exception('invalid data (len: %d)' % len(raw_data))
        self._raw_data = raw_data
        self._indices = _VARIABLE_INDICES_BASIC_SURVEY
        if indices:
            self._indices.update(indices)
    
    def get(self, key, default):
        try:
            return self[key]
        except KeyError:
            return default
    
    def __getitem__(self, key):
        return int(self._raw_data[slice(*(self._indices[key]))])
    
    def __str__(self):
        """Brief representation of this record"""
        return '[%15d.%5d] GESTCEN: %2d; PEAGE: %2d; PESEX: %2d' % (self.HRHHID, self.HRHHID2, self.GESTCEN, self.PEAGE, self.PESEX)


class CurrentPopulationSurveyInternetRecord(CurrentPopulationSurveyBasicRecord):
    '''Current Population Survey: extended interview (internet), single response.'''
    
    RECORD_LENGTH = 1260 #length of raw data
    
    def __init__(self, raw_data, indices=_VARIABLE_INDICES_INTERNET_SURVEY):
        if len(raw_data) < CurrentPopulationSurveyInternetRecord.RECORD_LENGTH:
            raise Exception('invalid data (len: %d)' % len(raw_data))
        super(CurrentPopulationSurveyInternetRecord, self).__init__(raw_data, indices)
    
    def __str__(self):
        """Brief representation of this record"""
        return '%s; HESCI5: %2d' % (super(CurrentPopulationSurveyInternetRecord, self).__str__(), self.HESCI5)

class Entity(object):
    def __init__(self, record):
        self._id = int("%d%d" % (record['HRHHID'], record['HRHHID2']))

class Household(Entity):
    #TODO: pass raw_data to household, let household track which variables to extract/store
    def __init__(self, record):
        super(Household, self).__init__(record)
        self._HUFINAL = record['HUFINAL']
        self._HEFAMINC = record['HEFAMINC']
        self._HRINTSTA = record['HRINTSTA']
        self._HRNUMHOU = record['HRNUMHOU']
        self._GESTCEN = record['GESTCEN']
        
        self._HESCI5 = record['HESCI5']
        self._HESCI6 = record['HESCI6']
        self._HESCI71 = record['HESCI71']
        self._HESCI72 = record['HESCI72']
        self._HESCI73 = record['HESCI73']
        self._HESCI74 = record['HESCI74']
        self._HESCI75 = record['HESCI75']
        self._HESCI76 = record['HESCI76']
        self._HESCI77 = record['HESCI77']
        self._HESCI8 = record['HESCI8']
        
        if self._HRINTSTA != 1:
            raise DataError(self._id, 'incomplete interview')
        
        self._members = []
    
    @property
    def id(self):
        """Unique identifier of this household."""
        return self._id
    
    @property
    def members(self):
        """List of household members."""
        return self._members
    
    @property
    def size(self):
        """Number of people in household (according to attribute in data)."""
        return self._HRNUMHOU
    
    def append(self, value):
        self._evaluate_person(value)
        self._members.append(value)
    
    def __getitem__(self, key):
        return self._members[key]
    
    def __setitem__(self, key, value):
        self._evaluate_person(value)
        self._members[key] = value
    
    def __len__(self):
        return len(self._members)
    
    def _evaluate_person(self, value):
        if value.household_id != self.id:
            raise Exception('invalid id (expected %d, received %d)' % (self.id, value.household_id))
    
    @property
    def outcome(self):
        """Final outcome description"""
        return _HUFINAL_DICT[self._HUFINAL].lower()
    
    @property
    def income(self):
        """Household income (discretized)"""
        return _HEFAMINC_DICT[self._HEFAMINC].lower()
    
    @property
    def status(self):
        return _HRINTSTA_DICT[self._HRINTSTA].lower()
    
    @property
    def state(self):
        """State"""
        return _GESTCEN_DICT[self._GESTCEN]
    
    @property
    def HESCI5(self):
        return 1 if 1 in [p.HESCI5 for p in self.members] else 2
    
    @property
    def HEFAMINC(self):
        return self._HEFAMINC
    
    @property
    def home_internet_access(self):
        """Does anyone in this household use the Internet from home?"""
        return 'Y' if 'Y' in [p.home_internet_access for p in self.members] else 'N'
    
    @property
    def home_internet_access_2(self):
        """Does anyone in this household use the Internet from home?"""
        return _HESCI5_DICT[self.HESCI5]
    
    def reason_for_no_broadband(self):
        yesses = [self._HESCI71]
        nos = [self._HESCI72, self._HESCI73, self._HESCI74, self._HESCI75, self._HESCI76, self._HESCI77]
        if yesses.count(1) != len(yesses) or nos.count(2) != len(nos):
            raise Exception('invalid property: person has broadband')
        return _HESCI8_DICT[self._HESCI8]
    
    def __str__(self):
        """Brief representation of this household"""
        return '[%20d]: members: %2d; state: %s; home internet access: %s (%s)' % (self.id, len(self), self.state,
                self.home_internet_access, self.home_internet_access_2)


class Person(Entity):
    def __init__(self, record):
        super(Person, self).__init__(record)
        #TODO; maintain list of variable names, set attributes from list
        self._PEAGE = record['PEAGE']
        self._PESEX = record['PESEX']
        self._PEEDUCA = record['PEEDUCA']
        self._PEIO1ICD = record['PEIO1ICD']
        self._PEIO2ICD = record['PEIO2ICD']
        self._PESCI1 = record['PESCI1']
        self._PEPERSCR = record['PEPERSCR']
        self._PEHOME = record['PEHOME']
        self._PESCHL = record['PESCHL']
        self._PEWRKA = record['PEWRKA']
        self._PEDESK = record['PEDESK']
        self._PELAPT = record['PELAPT']
        self._PETABL = record['PETABL']
        self._PECELL = record['PECELL']
        self._PEGAME = record['PEGAME']
        self._PEPR3A5 = record['PEPR3A5']
        self._PEPRIM7 = record['PEPRIM7']
        self._PEPRIM10 = record['PEPRIM10']
        self._PEPRM151 = record['PEPRM151']
        self._PEPRM152 = record['PEPRM152']
        self._PEPRM153 = record['PEPRM153']
        self._PEPRM154 = record['PEPRM154']
        self._PEPRM155 = record['PEPRM155']
        self._PEPRM156 = record['PEPRM156']
        
        self._GESTCEN = record['GESTCEN']
        self.HESCI5 = record['HESCI5']
        self.HEFAMINC = record['HEFAMINC']
    
    def get_attributes(self):
        return {
            'PEAGE': self._PEAGE,
            'PESEX': self._PESEX,
            'PEEDUCA': self._PEEDUCA,
            'PEIO1ICD': self._PEIO1ICD,
            'PEIO2ICD': self._PEIO2ICD,
            'PESCI1': self._PESCI1,
            'PEPERSCR': self._PEPERSCR,
            'PEHOME': self._PEHOME,
            'PESCHL': self._PESCHL,
            'PEWRKA': self._PEWRKA,
            'PEDESK': self._PEDESK,
            'PELAPT': self._PELAPT,
            'PETABL': self._PETABL,
            'PECELL': self._PECELL,
            'PEGAME': self._PEGAME,
            'PEPR3A5': self._PEPR3A5,
            'PEPRIM7': self._PEPRIM7,
            'PEPRIM10': self._PEPRIM10,
            'PEPRM151': self._PEPRM151,
            'PEPRM152': self._PEPRM152,
            'PEPRM153': self._PEPRM153,
            'PEPRM154': self._PEPRM154,
            'PEPRM155': self._PEPRM155,
            'PEPRM156': self._PEPRM156
        }
    
    @property
    def household_id(self):
        return self._id
    
    @property
    def state(self):
        """State"""
        return _GESTCEN_DICT[self._GESTCEN]
    
    @property
    def age(self):
        """Person's age."""
        return self._PEAGE
    
    @property
    def sex(self):
        """Person's sex."""
        return _PESEX_DICT[self._PESEX]
    
    @property
    def education(self):
        """Person's educational attainment."""
        return _PEEDUCA_DICT[self._PEEDUCA]
    
    @property
    def occupation(self):
        return _PEIOXICD_DICT[self._PEIO1ICD]
    
    @property
    def occupation_alt(self):
        return _PEIOXICD_DICT[self._PEIO2ICD]
    
    @property
    def mobile_phone(self):
        """Do you use a mobile phone?"""
        return _PESCI1_DICT[self._PESCI1]
    
    @property
    def internet_access(self):
        return _PEPERSCR_DICT[self._PEPERSCR]
    
    @property
    def home_internet_access(self):
        if self.internet_access == 'N' or _HESCI5_DICT[self.HESCI5] == 'N':
            return 'N'
        return _PEHOME_DICT[self._PEHOME]
    
    @property
    def work_internet_access(self):
        if self.internet_access == 'N' or _HESCI5_DICT[self.HESCI5] == 'N':
            return 'N'
        return _PEWRKA_DICT[self._PEWRKA]
    
    @property
    def desktop(self):
        """Do you use a desktop PC to access the Internet?"""
        return _PEDESK_DICT[self._PEDESK]
    
    @property
    def laptop(self):
        """Do you use a laptop or netbook computer to access the Internet?"""
        return _PELAPT_DICT[self._PELAPT]
    
    @property
    def tablet(self):
        """Do you use a tablet or ebook reader to access the Internet?"""
        return _PETABL_DICT[self._PETABL]
    
    @property
    def mobile(self):
        """Do you use a cellular or smartphone to access the Internet?"""
        return _PECELL_DICT[self._PECELL]
    
    @property
    def console(self):
        """Do you use a game system or console such as Wii, XBOX, or Playstation to access the Internet?"""
        return _PEGAME_DICT[self._PEGAME]
    
    @property
    def school_internet_access(self):
        if self.internet_access == 'N' or _HESCI5_DICT[self.HESCI5] == 'N':
            return 'N'
        return _PESCHL_DICT[self._PESCHL]
    
    @property
    def job_search_on_internet(self):
        """Do you use the Internet when conducting a job search (research listings, post a resume, and so on)?"""
        return _PEPRIM7_DICT[self._PEPRIM7]
    
    @property
    def educational_internet_usage(self):
        """Do you rely on the Internet for education or schoolwork (such as taking a class online)?"""
        return _PEPR3A5_DICT[self._PEPR3A5]
    
    @property
    def research_health_information(self):
        """Do you use the Internet to research health information for self-diagnosis or treatment?"""
        return _PEPRIM10_DICT[self._PEPRIM10]
    
    @property
    def news_television(self):
        """Do you obtain news or other information through watching TV?"""
        return _PEPRM15X_DICT[self._PEPRM151]
    
    @property
    def news_radio(self):
        """Do you obtain news or other information through listening to radio?"""
        return _PEPRM15X_DICT[self._PEPRM152]
    
    @property
    def news_internet(self):
        """Do you obtain news or other information through the Internet?"""
        return _PEPRM15X_DICT[self._PEPRM153]
    
    @property
    def news_print(self):
        """Do you obtain news or other information through print newspaper or magazines?"""
        return _PEPRM15X_DICT[self._PEPRM154]
    
    @property
    def news_conversation(self):
        """Do you obtain news or other information through conversing with friends/family/co-workers?"""
        return _PEPRM15X_DICT[self._PEPRM155]
    
    @property
    def news_other(self):
        """Do you obtain news or other information through other means?"""
        return _PEPRM15X_DICT[self._PEPRM156]
    
    def __str__(self):
        return "age: %2d; sex: %s; home internet access: %s" % (self.age, self.sex, self.home_internet_access)
